#include <sys/types.h> 
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "queue.h"

int max_size(int fd);
int rename_data_file(const char* oldname, int index);

QueueErr append(void *ptr, size_t size, int index);

QueueErr append(void *ptr, size_t size, int index)
{
	int code, fd;
start_save:
	if(handler[index].wfd == 0)
	{
		fd = open(handler[index].name, O_RDWR|O_APPEND|O_CREAT, 0644);
		if(-1 == fd) goto hard_disk_error;
		
		handler[index].wfd = fd;
	}
	
	if(1 == max_size(handler[index].wfd))
	{
		close(handler[index].wfd);
		handler[index].wfd = 0;
		
		if(0 > (code = rename_data_file(handler[index].name, index)))
		{
			fprintf(stderr, "-2 Rename file name Error, code :%d at %d in '%s'.\n",
				code, __LINE__, __FILE__);
			
			return QERR_RENAME_FILE;
		}
		goto start_save;
	}
	
	if(-1 != lockf(handler[index].wfd, F_LOCK, 0) &&
		-1 == write(handler[index].wfd, ptr, size))
	{
		goto hard_disk_error;
	}
	
	lockf(handler[index].wfd, F_ULOCK, 0);
	if(is_rqueue_lock[index] == 1) rqueue_read_unlock(index);
	return Q_EXEC_OK;

hard_disk_error:
	fprintf(stderr, "-3 Hard disk Error at %d in '%s'.\n", __LINE__, __FILE__);
	return QERR_HARD_DISK;
}

QueueErr wqueue_append(queue_header *header, const char* content, const char* path)
{
	unsigned char *ptr, *pp;
	size_t size;
	QueueErr code;
	int index = QUEUE_INDEX_NONE;
	     if(0 == strcmp(path, QUEUE_PATH_HTTP))  index = QUEUE_INDEX_HTTP;
	else if(0 == strcmp(path, QUEUE_PATH_SHM ))  index = QUEUE_INDEX_SHM; 
	else if(0 == strcmp(path, QUEUE_PATH_MSG ))  index = QUEUE_INDEX_MSG;
	else if(0 == strcmp(path, QUEUE_PATH_FIFO))  index = QUEUE_INDEX_FIFO;
	else if(0 == strcmp(path, QUEUE_PATH_DB  ))  index = QUEUE_INDEX_DB;
	else if(0 == strcmp(path, QUEUE_PATH_INNER)) index = QUEUE_INDEX_INNER;
	else if(0 == strncmp(path, QUEUE_PATH_DEAULT, strlen(QUEUE_PATH_DEAULT))) {
		strncpy(header->ext, path + strlen(QUEUE_PATH_DEAULT), HEADER_EXT_LENGTH - 1);
		index = QUEUE_INDEX_EXTER;
	}

	if(index == QUEUE_INDEX_NONE)
		return QERR_CMD_UNKNOWN;

	size = header->size + sizeof(queue_header);
	if(NULL == (ptr = pp = (unsigned char*)malloc(size)))
		return QERR_MEM_FAIL;

	memcpy(ptr, header, sizeof(queue_header));
	memcpy(ptr + sizeof(queue_header), content, header->size);
	code = append(pp, size, index);
	free(pp);
	return code;
}

int max_size(int fd)
{
	struct stat buffer;
	if(-1 == fstat(fd, &buffer))
		return -1;
	return buffer.st_size > MAX_FILE_SIZE;
}

int rename_data_file(const char* oldname, int index)
{
	char nname[FILE_PATH_SIZE];		
	if(status[index]->is_lock)
	{
		/* queue has been locked. */
		usleep(200000); 
		return 0;
	}
	
	status[index]->is_lock = 1;
	if(-1 == msync(status[index], sizeof(queue_status), MS_SYNC))
		return -2;
	
	/* waiting to finish rqueue handing. */
	usleep(100000);
	
	status[index]->id_max++;
	sprintf(nname, "%s.%d", oldname, status[index]->id_max);
	if(-1 == rename(oldname, nname))
	{
		status[index]->id_max--;
		return -3;
	}
	
	if(-1 == status[index]->id_next)
	{
		status[index]->id_now = status[index]->id_max;
		status[index]->id_next = 0;
	}
	else if(0 == status[index]->id_next)
	{
		status[index]->id_next = status[index]->id_max;
	}
	
	status[index]->position = 0;
	status[index]->is_lock = 0;
	if(-1 == msync(status[index], sizeof(queue_status), MS_SYNC))
		return -2;

	return 1;
}
